Skip to content

[fix](table stream) force VCollectIterator merge in case of binlog scan - #66338

Open
TsukiokaKogane wants to merge 5 commits into
apache:masterfrom
TsukiokaKogane:fix_append_only_multi_seg_core
Open

[fix](table stream) force VCollectIterator merge in case of binlog scan #66338
TsukiokaKogane wants to merge 5 commits into
apache:masterfrom
TsukiokaKogane:fix_append_only_multi_seg_core

Conversation

@TsukiokaKogane

@TsukiokaKogane TsukiokaKogane commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: close #65901

Related PR: #xxx

Problem Summary:

Querying an APPEND_ONLY table stream backed by ROW binlog crashes BE with Check failed: !_get_data_by_ref when a data rowset holds multiple segments.

Reproduction: create a DUP or MoW table with ROW binlog enabled, create an APPEND_ONLY stream on it, load enough non-overlapping data so that a rowset is classified as NONOVERLAPPING but contains more than one segment, then SELECT
... FROM stream. It does not reproduce with single-segment rowsets.

Root cause: a table-stream scan forces force_key_ordered_read = true, so a rowset with more than one segment makes BetaRowsetReader::is_merge_iterator() return true. Because the rowsets are NONOVERLAPPING, the outer VCollectIterator
picks block-batch mode (_merge == false) and goes through the union path. However, Level0Iterator::init_for_union() still computed _get_data_by_ref = get_data_by_ref && _is_merge_iterator, so a non-first child was put into by-
reference mode. _get_data_by_ref is only meaningful on the merge path (where init() prefetches the block view and the child is consumed row by row through the merge heap); the union path always reads whole blocks via next(Block*) /
ensure_first_row_ref(), both of which assert !_get_data_by_ref. The two independently selected modes conflict and the assertion aborts BE.

Fix: on the non-merge union path, Level0Iterator::init_for_union() now always sets _get_data_by_ref = false, keeping every child in block-batch mode consistent with the union path. The merge path (rowsets overlapping) goes through
init() and is unaffected.

Before: BE aborts with SIGABRT (Check failed: !_get_data_by_ref) at VCollectIterator::Level0Iterator::ensure_first_row_ref() / next(Block*).
After: the stream query completes and returns all rows correctly.

Release note

None

Check List (For Author)

  • Test

    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason
  • Behavior changed:

    • No.
    • Yes.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@TsukiokaKogane

Copy link
Copy Markdown
Contributor Author

run buildall

@TsukiokaKogane TsukiokaKogane changed the title [fix](table stream) Forbid child iterator by-ref mode on the non-merg… [fix](table stream) Forbid child iterator by-ref mode on the non-merge union path Jul 31, 2026
@TsukiokaKogane
TsukiokaKogane marked this pull request as draft July 31, 2026 11:15
@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 29218 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 44b3c979a9d9ffdb2a99f285cefb2096d8d3dd75, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17704	3980	3966	3966
q2	2007	325	204	204
q3	10270	1442	799	799
q4	4681	467	338	338
q5	7507	828	554	554
q6	176	166	135	135
q7	736	800	597	597
q8	9314	1620	1517	1517
q9	5366	4052	4041	4041
q10	6758	1635	1364	1364
q11	494	362	336	336
q12	717	580	463	463
q13	18104	3212	2724	2724
q14	264	251	238	238
q15	q16	742	731	661	661
q17	1027	946	952	946
q18	6774	5754	5667	5667
q19	1318	1295	1013	1013
q20	828	688	577	577
q21	6344	2868	2751	2751
q22	461	369	327	327
Total cold run time: 101592 ms
Total hot run time: 29218 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4991	4480	4649	4480
q2	287	329	227	227
q3	4888	5264	4694	4694
q4	2185	2254	1445	1445
q5	4680	4410	4374	4374
q6	225	174	128	128
q7	1961	1708	1522	1522
q8	2314	2037	1994	1994
q9	7110	6653	6685	6653
q10	4276	4182	3793	3793
q11	500	369	336	336
q12	696	698	491	491
q13	2946	3267	2720	2720
q14	263	282	248	248
q15	q16	661	688	597	597
q17	1232	1213	1202	1202
q18	7258	6847	6761	6761
q19	1069	1025	1104	1025
q20	2191	2190	1921	1921
q21	5624	4575	4444	4444
q22	504	439	410	410
Total cold run time: 55861 ms
Total hot run time: 49465 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 169216 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 44b3c979a9d9ffdb2a99f285cefb2096d8d3dd75, data reload: false

query5	4337	603	490	490
query6	460	217	211	211
query7	4927	626	334	334
query8	328	177	166	166
query9	8762	4037	4022	4022
query10	444	353	292	292
query11	5808	2203	1998	1998
query12	155	102	95	95
query13	1291	612	446	446
query14	6133	4663	4379	4379
query14_1	3829	3806	3796	3796
query15	212	207	175	175
query16	1007	485	479	479
query17	1108	694	571	571
query18	2481	466	345	345
query19	208	194	149	149
query20	116	106	102	102
query21	231	158	135	135
query22	13156	13131	12829	12829
query23	17256	16444	16011	16011
query23_1	16123	16131	16117	16117
query24	7535	1662	1228	1228
query24_1	1270	1267	1232	1232
query25	565	495	374	374
query26	1321	373	215	215
query27	2593	613	382	382
query28	4445	2021	2039	2021
query29	1096	665	466	466
query30	340	257	234	234
query31	1092	1078	960	960
query32	101	61	60	60
query33	533	300	235	235
query34	1186	1111	636	636
query35	732	737	648	648
query36	773	778	712	712
query37	147	107	93	93
query38	1831	1652	1601	1601
query39	829	853	787	787
query39_1	784	778	785	778
query40	246	165	138	138
query41	65	63	63	63
query42	93	93	95	93
query43	316	319	278	278
query44	1405	768	764	764
query45	180	175	171	171
query46	1010	1193	689	689
query47	1548	1543	1443	1443
query48	389	385	295	295
query49	598	405	299	299
query50	1057	422	348	348
query51	10563	10364	10419	10364
query52	85	83	73	73
query53	263	290	197	197
query54	288	235	213	213
query55	74	70	66	66
query56	291	296	292	292
query57	1025	995	915	915
query58	285	258	250	250
query59	1555	1607	1352	1352
query60	327	264	247	247
query61	157	147	138	138
query62	394	321	275	275
query63	242	198	191	191
query64	2816	1012	837	837
query65	3858	3864	3818	3818
query66	1824	468	366	366
query67	28232	28148	28082	28082
query68	3167	1510	990	990
query69	389	299	266	266
query70	909	779	778	778
query71	364	326	322	322
query72	2961	2603	2344	2344
query73	850	767	418	418
query74	4605	4515	4309	4309
query75	2404	2399	1982	1982
query76	2321	1159	752	752
query77	339	388	268	268
query78	11212	11197	10644	10644
query79	1367	1153	756	756
query80	700	556	446	446
query81	472	326	284	284
query82	554	150	120	120
query83	385	321	295	295
query84	271	159	131	131
query85	954	622	533	533
query86	318	253	217	217
query87	1805	1792	1767	1767
query88	3692	2827	2788	2788
query89	402	320	284	284
query90	1861	195	195	195
query91	212	192	159	159
query92	65	62	57	57
query93	1634	1641	944	944
query94	586	349	302	302
query95	779	497	555	497
query96	1055	770	350	350
query97	2442	2472	2359	2359
query98	202	197	193	193
query99	722	716	613	613
Total cold run time: 254849 ms
Total hot run time: 169216 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 23.94 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 44b3c979a9d9ffdb2a99f285cefb2096d8d3dd75, data reload: false

query1	0.01	0.01	0.00
query2	0.09	0.06	0.05
query3	0.26	0.13	0.14
query4	1.61	0.15	0.13
query5	0.23	0.21	0.21
query6	1.16	0.82	0.81
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.37	0.31	0.30
query10	0.54	0.55	0.58
query11	0.20	0.15	0.13
query12	0.17	0.14	0.14
query13	0.47	0.47	0.47
query14	1.01	1.02	0.98
query15	0.60	0.59	0.59
query16	0.32	0.33	0.33
query17	1.16	1.06	1.09
query18	0.22	0.20	0.20
query19	2.11	1.92	1.92
query20	0.02	0.01	0.01
query21	15.44	0.23	0.13
query22	4.89	0.04	0.05
query23	16.11	0.29	0.14
query24	2.96	0.46	0.32
query25	0.10	0.05	0.04
query26	0.73	0.21	0.16
query27	0.05	0.03	0.04
query28	3.55	0.86	0.35
query29	12.52	4.04	3.21
query30	0.27	0.17	0.16
query31	2.76	0.56	0.31
query32	3.21	0.60	0.49
query33	3.15	3.30	3.20
query34	15.48	3.95	3.30
query35	3.22	3.23	3.24
query36	0.55	0.45	0.41
query37	0.09	0.07	0.06
query38	0.06	0.04	0.04
query39	0.04	0.04	0.03
query40	0.17	0.15	0.15
query41	0.09	0.03	0.03
query42	0.04	0.02	0.03
query43	0.04	0.03	0.04
Total cold run time: 96.17 s
Total hot run time: 23.94 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (1/1) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 75.83% (32040/42251)
Line Coverage 60.46% (357186/590798)
Region Coverage 56.92% (299095/525493)
Branch Coverage 58.30% (134689/231038)

@TsukiokaKogane
TsukiokaKogane marked this pull request as ready for review August 4, 2026 08:16
@TsukiokaKogane

Copy link
Copy Markdown
Contributor Author

run buildall

@HappenLee

Copy link
Copy Markdown
Contributor

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues need resolution; see the inline comments.

Critical checkpoint conclusions:

  • Goal and proof: The APPEND_ONLY crash trigger is correctly identified, and the DUP/MoW sections cover that direct block path. However, the blanket union-mode change leaves the supported MIN_DELTA/DETAIL row-wise child handoff able to crash (P1), so the fix is not complete across parallel consumers. The schema-derived is_unique change is correct, but its distinct event-preservation behavior has no direct regression oracle (P2).
  • Scope and parallel paths: The production edits are localized. Ordinary/projected UNIQUE reads, AGG paths, row-binlog DUP semantics, compaction, schema-change, checksum, and cloud/manual uniqueness contexts were traced. No storage-format, protocol, or rolling-upgrade incompatibility was introduced.
  • Concurrency, lifecycle, and errors: The iterator state is scanner-local and sequential; no new threads, locks, atomics, or lock-order concerns apply. The surviving defect is lifecycle initialization at union-child handoff: a later child can reach a null block before reader status propagation. Shared-block ownership, same bits, EOF handling, row locations, reverse traversal, and empty-child erasure are otherwise coherent once the child is initialized.
  • Configuration and observability: No production configuration is added. The regression's temporary BE setting and debug point are scoped and restored, and the non-cloud restriction matches existing row-binlog support. No new metric or log is required for this local correction.
  • Tests and results: The added output is internally consistent and deterministically ordered for the APPEND_ONLY cases. Coverage is missing for a row-wise multi-rowset handoff and for same-key/same-TSO events split across segments of one forced-merge rowset. No build or test was run during this static-only review.
  • Persistence and writes: The production change is read-time only; it adds no transaction, persistence, edit-log, write-atomicity, or FE-BE variable work.
  • Performance: No separate substantiated performance blocker survived review; correctness initialization must be fixed first.
  • User focus: No additional user-provided focus was present, so the whole PR received the normal complete review.

Review completion: complete after two review rounds; all candidates were accepted, deduplicated, or dismissed before freezing this request-changes payload.

// CHECK(!_get_data_by_ref). See issue #65901.
_is_merge_iterator = _rs_reader->is_merge_iterator();
_get_data_by_ref = get_data_by_ref && _is_merge_iterator;
_get_data_by_ref = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Initialize later block-mode union children before row-wise handoff

This forces every union child into block mode, but init_for_union() still does not allocate _block or initialize _ref for children after the first (only the first non-empty child reaches ensure_first_row_ref()). When a row-wise consumer such as MIN_DELTA/DETAIL advances to a later multi-segment child, next(IteratorRowRef*) takes the new false branch and evaluates _block->rows() before refresh_current_row() can lazily allocate it, so a pair of key-disjoint multi-segment rowsets can null-dereference at child handoff. Please establish a valid empty block/pre-first-row state for every child entering block mode (resetting cursors alone is insufficient), and cover a row-wise union consumer as well as APPEND_ONLY.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking into it

// every row is an independent change event that must not be deduplicated by user
// key; using tablet()->keys_type() (UNIQUE_KEYS for MoW/UNIQUE tables) would make
// the rowset merge iterator drop events across segments. Mirrors VerticalBlockReader.
_reader_context.is_unique = _tablet_schema->keys_type() == UNIQUE_KEYS;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Add coverage for equal keys across segments of one rowset

This flag is consumed by BetaRowsetReader only when its per-rowset VMergeIterator compares segments. Every new load here is internally key-unique; Section C overlaps two separate rowsets/Tsos, so the outer VCollectIterator handles it and the suite still passes if only this line is reverted. The older raw binlog() multi-segment test also normally uses scan type NONE and does not force this internal merge. Please add one table-stream load whose same key/TSO events cross a forced segment boundary and assert the retained event chain, so the event-loss behavior fixed by this line has a direct regression.

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28727 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 690d6942ce7fcb6b930d5b7c7f27bf9700b9c82e, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17707	4040	3941	3941
q2	2053	321	200	200
q3	10279	1438	804	804
q4	4683	464	343	343
q5	7554	837	552	552
q6	176	167	134	134
q7	741	797	603	603
q8	9337	1539	1541	1539
q9	5376	4047	4111	4047
q10	6718	1629	1352	1352
q11	493	369	337	337
q12	709	575	456	456
q13	18109	3338	2771	2771
q14	267	258	233	233
q15	q16	745	736	664	664
q17	1032	981	928	928
q18	6632	5623	5547	5547
q19	1361	1197	1047	1047
q20	767	705	565	565
q21	5880	2590	2367	2367
q22	425	355	297	297
Total cold run time: 101044 ms
Total hot run time: 28727 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4266	4177	4184	4177
q2	278	319	208	208
q3	4594	4985	4436	4436
q4	2158	2253	1409	1409
q5	4224	4108	4104	4104
q6	225	171	125	125
q7	1687	1641	1400	1400
q8	2718	2252	2169	2169
q9	7374	7441	7374	7374
q10	4329	4256	3880	3880
q11	560	421	368	368
q12	756	734	520	520
q13	3289	3516	2887	2887
q14	314	318	273	273
q15	q16	705	740	680	680
q17	1305	1262	1266	1262
q18	12104	11056	11854	11056
q19	1133	1144	1173	1144
q20	2228	2270	1933	1933
q21	5612	4784	4955	4784
q22	597	489	431	431
Total cold run time: 60456 ms
Total hot run time: 54620 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 166289 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 690d6942ce7fcb6b930d5b7c7f27bf9700b9c82e, data reload: false

query5	4299	586	457	457
query6	479	212	196	196
query7	4852	590	346	346
query8	322	157	157	157
query9	8775	4099	4091	4091
query10	459	344	319	319
query11	5829	2186	2002	2002
query12	151	100	95	95
query13	1261	575	448	448
query14	6029	4239	3928	3928
query14_1	3771	3749	3760	3749
query15	194	194	175	175
query16	985	452	427	427
query17	897	686	524	524
query18	2435	444	321	321
query19	198	183	137	137
query20	105	98	104	98
query21	235	154	132	132
query22	13050	13094	12779	12779
query23	15763	14961	14555	14555
query23_1	15441	15090	15087	15087
query24	7677	1720	1201	1201
query24_1	1243	1250	1232	1232
query25	548	456	386	386
query26	1317	353	218	218
query27	2614	577	389	389
query28	4609	2079	2036	2036
query29	1074	577	474	474
query30	331	266	219	219
query31	1153	1112	1023	1023
query32	108	55	56	55
query33	512	288	235	235
query34	1192	1152	636	636
query35	713	739	621	621
query36	776	765	704	704
query37	147	97	87	87
query38	1822	1770	1661	1661
query39	831	826	796	796
query39_1	819	769	774	769
query40	246	157	138	138
query41	62	58	58	58
query42	90	86	88	86
query43	317	318	275	275
query44	1423	770	765	765
query45	177	167	164	164
query46	1007	1181	709	709
query47	1516	1546	1438	1438
query48	401	427	290	290
query49	593	420	304	304
query50	1046	444	316	316
query51	10383	10658	10302	10302
query52	82	85	73	73
query53	246	267	191	191
query54	269	226	225	225
query55	72	73	66	66
query56	295	280	286	280
query57	1023	1003	914	914
query58	275	253	255	253
query59	1495	1557	1368	1368
query60	296	275	264	264
query61	152	142	148	142
query62	396	316	271	271
query63	223	198	194	194
query64	2844	1021	858	858
query65	3897	3816	3793	3793
query66	1856	469	357	357
query67	28119	28122	28061	28061
query68	3374	1594	1056	1056
query69	418	296	254	254
query70	876	780	771	771
query71	371	347	318	318
query72	3044	2627	2421	2421
query73	857	837	439	439
query74	4609	4501	4307	4307
query75	2385	2336	1998	1998
query76	2283	1148	751	751
query77	339	366	269	269
query78	11180	11083	10587	10587
query79	1377	1185	732	732
query80	737	530	476	476
query81	466	329	283	283
query82	628	176	134	134
query83	402	328	306	306
query84	318	161	140	140
query85	965	593	520	520
query86	347	228	218	218
query87	1976	1939	1833	1833
query88	3695	2814	2779	2779
query89	388	321	280	280
query90	1773	192	193	192
query91	196	187	162	162
query92	60	57	53	53
query93	1554	1556	970	970
query94	591	343	313	313
query95	780	578	470	470
query96	1091	785	356	356
query97	2434	2476	2317	2317
query98	189	184	181	181
query99	723	737	614	614
Total cold run time: 252431 ms
Total hot run time: 166289 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 23.9 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 690d6942ce7fcb6b930d5b7c7f27bf9700b9c82e, data reload: false

query1	0.00	0.00	0.01
query2	0.09	0.05	0.04
query3	0.25	0.14	0.13
query4	1.61	0.14	0.13
query5	0.26	0.21	0.22
query6	1.16	0.84	0.81
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.36	0.32	0.31
query10	0.54	0.55	0.58
query11	0.19	0.13	0.15
query12	0.17	0.14	0.14
query13	0.46	0.47	0.47
query14	1.02	1.01	0.98
query15	0.60	0.59	0.58
query16	0.32	0.32	0.32
query17	1.09	1.08	1.12
query18	0.22	0.21	0.19
query19	2.08	1.88	1.92
query20	0.02	0.01	0.01
query21	15.47	0.21	0.15
query22	4.79	0.05	0.06
query23	16.12	0.31	0.12
query24	3.02	0.46	0.31
query25	0.10	0.05	0.05
query26	0.74	0.22	0.15
query27	0.04	0.04	0.03
query28	3.50	0.77	0.34
query29	12.47	4.06	3.17
query30	0.27	0.15	0.16
query31	2.76	0.57	0.31
query32	3.22	0.66	0.54
query33	3.16	3.26	3.28
query34	15.58	3.97	3.28
query35	3.19	3.19	3.25
query36	0.55	0.43	0.42
query37	0.08	0.06	0.06
query38	0.05	0.04	0.04
query39	0.03	0.03	0.03
query40	0.17	0.16	0.15
query41	0.09	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.03	0.03
Total cold run time: 96.02 s
Total hot run time: 23.9 s

@TsukiokaKogane TsukiokaKogane changed the title [fix](table stream) Forbid child iterator by-ref mode on the non-merge union path [fix](table stream) force VCollectIterator merge in case of binlog scan Aug 5, 2026
@TsukiokaKogane

Copy link
Copy Markdown
Contributor Author

run buildall

@TsukiokaKogane

Copy link
Copy Markdown
Contributor Author

run buildall

@@ -60,9 +60,9 @@ class BetaRowsetReader : public RowsetReader {
}

bool is_merge_iterator() const override {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果这里只有一个segment的话,这个force是不是没有必要

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug](Highest) APPEND_ONLY table stream query can crash BE in VCollectIterator with multi-segment rowsets

3 participants